import java.awt.AWTException;
import java.awt.CheckboxMenuItem;
import java.awt.Image;
import java.awt.Menu;
import java.awt.MenuItem;
import java.awt.MouseInfo;
import java.awt.PointerInfo;
import java.awt.PopupMenu;
import java.awt.SystemTray;
import java.awt.Toolkit;
import java.awt.TrayIcon;
import java.awt.TrayIcon.MessageType;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.ItemEvent;
import java.awt.event.ItemListener;
import java.net.URL;
import java.util.ArrayList;
import java.util.List;

/**
 * Track mouse movement and remind user to blink when they have been 
 * active for a given period of time.
 */
public class Blinky {
	public static final long DEFAULT_TIME_TO_BLINK_MS = 1000l * 60l * 15l; // every 15 minutes
	public static final long DEFAULT_TIME_TO_SHOW_BLINK_NOTIFICATION_MS = 1000l * 4l; // 4 seconds

	private long blinkInterval = DEFAULT_TIME_TO_BLINK_MS;

	final ActivityTracker timer = new ActivityTracker();
	final BlinkNotifier notifier = new BlinkNotifier();

	public Blinky() {

	}

	public void start() {
		boolean noExit = true;

		if (SystemTray.isSupported()) {
			SystemTray tray = SystemTray.getSystemTray();

			// load an image
			URL url = Blinky.class.getResource("/eyeball.gif"); 
			Image image = Toolkit.getDefaultToolkit().getImage(url);

			long delayTimes[] = { 1000l * 5l, 1000l * 30l, 1000l * 60l * 5l,
					1000l * 60l * 30l };
			PopupMenu popup = buildPopupMenuForDelays(delayTimes);

			// construct a TrayIcon
			final TrayIcon trayIcon = new TrayIcon(image, "Blinky", popup);

			// add the tray image
			try {
				tray.add(trayIcon);
			} catch (AWTException e) {
				System.err.println(e);
			}
			
			trayIcon.displayMessage("Blinky Started", "Default blink interval (during usage) is " + getDescriptiveBlinkInterval(blinkInterval), MessageType.INFO);
			
			// Track Mouse movements
			double oldX = -1;
			double oldY = -1;

			while (noExit) {
				// Have to poll for mouse position (across whole screen)
				// Too bad there's no event for it.
				PointerInfo pointer = MouseInfo.getPointerInfo();

				double newX = pointer.getLocation().getX();
				double newY = pointer.getLocation().getY();

				if (newX != oldX || newY != oldY) // if mouse moved since last
													// poll
				{
					timer.registerActivity();

					oldX = newX;
					oldY = newY;
				}

				if (timer.getActiveTimeMs() > getBlinkInterval()) {
					notifier.tellThemToBlink(DEFAULT_TIME_TO_SHOW_BLINK_NOTIFICATION_MS);
					timer.reset();
				}

				try {
					Thread.sleep(200l); // yield to operating system for 1/5
										// second
				} catch (InterruptedException e) {
				}

				// System.out.println("active time = " + timer.getActiveTimeMs()
				// + ", idle time = " + timer.getIdleTimeMs());
			}

			tray.remove(trayIcon);
		}
	}

	private PopupMenu buildPopupMenuForDelays(long[] delayTimes) {
		PopupMenu popup = new PopupMenu();

		Menu activityTimeMenu = new Menu("Activity Interval between Blinks (in ms)");

		final List<CheckboxMenuItem> delayCheckBoxItems = new ArrayList<CheckboxMenuItem>();

		ItemListener listener = new ItemListener() {
			public void itemStateChanged(ItemEvent e) {
				String delayMs = e.getItem().toString();
				
				System.out.println("Changing blink interval to = " + getDescriptiveBlinkInterval(Long.parseLong(delayMs)));

				for (int x = 0; x < delayCheckBoxItems.size(); x++) {
					if (delayCheckBoxItems.get(x) == e.getSource()) {
						delayCheckBoxItems.get(x).setState(true); // checkbox
																	// on
					} else {
						delayCheckBoxItems.get(x).setState(false); // checkbox
																	// off
					}
				}

				timer.reset();
				setBlinkInterval(Long.parseLong(delayMs));
			}

		};

		for (int x = 0; x < delayTimes.length; x++) {
			CheckboxMenuItem item = new CheckboxMenuItem(delayTimes[x] + "");
			item.addItemListener(listener);
			delayCheckBoxItems.add(item);
			activityTimeMenu.add(item);
		}

		popup.add(activityTimeMenu);

		ActionListener forceListener = new ActionListener() {
			public void actionPerformed(ActionEvent e) {
				notifier.tellThemToBlink(DEFAULT_TIME_TO_SHOW_BLINK_NOTIFICATION_MS);
			}
		};

		MenuItem forceItem = new MenuItem("Force Blink");
		forceItem.addActionListener(forceListener);

		ActionListener exitListener = new ActionListener() {
			public void actionPerformed(ActionEvent e) {
				System.exit(0);
			}
		};

		popup.add(forceItem);

		MenuItem exitItem = new MenuItem("Exit");
		exitItem.addActionListener(exitListener);

		popup.add(exitItem);

		return popup;
	}

	protected void setBlinkInterval(long l) {
		blinkInterval = l;
		timer.reset();
	}

	protected long getBlinkInterval() {
		return blinkInterval;
	}

	public static void main(String args[]) {
		Blinky blinky = new Blinky();
		blinky.start();
	}
	
	private String getDescriptiveBlinkInterval(long blinkInterval)
	{
		return blinkInterval + " ms (" + (blinkInterval / 1000) + " s ) (" + (blinkInterval / 1000 / 60) + " m)";
	}
	
}
